/*-------------------------------------------------------------------------
 *
 * name.c
 *	  内置类型 "name" 的函数。
 *
 * name 替代了 char16，并且经过仔细实现，使其成为
 * 物理长度为 NAMEDATALEN 的字符串。
 * 不要在任何地方使用硬编码常量，
 * 始终使用 NAMEDATALEN 作为符号常量！   - jolly 8/21/95
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/name.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/namespace.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/varlena.h"


/*****************************************************************************
 *	 用户输入/输出例程 (无)												 *
 *****************************************************************************/


/*
 *		namein	- 将 "..." 转换为内部表示
 *
 *		注意：
 *				[旧] 当前如果 strlen(s) < NAMEDATALEN，额外字符将是空值
 *				现在始终以 NULL 结束
 */
Datum namein(PG_FUNCTION_ARGS)
{
	char	   *fc_s = PG_GETARG_CSTRING(0);
	Name		fc_result;
	int			fc_len;

	fc_len = strlen(fc_s);

	/* 截断超长输入 */
	if (fc_len >= NAMEDATALEN)
		fc_len = pg_mbcliplen(fc_s, fc_len, NAMEDATALEN - 1);

	/* 我们在这里使用 palloc0 以确保结果是零填充的 */
	fc_result = (Name) palloc0(NAMEDATALEN);
	memcpy(NameStr(*fc_result), fc_s, fc_len);

	PG_RETURN_NAME(fc_result);
}

/*
 *		nameout - 将内部表示转换为 "..."
 */
Datum nameout(PG_FUNCTION_ARGS)
{
	Name		fc_s = PG_GETARG_NAME(0);

	PG_RETURN_CSTRING(pstrdup(NameStr(*fc_s)));
}

/*
 *		namerecv			- 将外部二进制格式转换为名称
 */
Datum namerecv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	Name		fc_result;
	char	   *fc_str;
	int			fc_nbytes;

	fc_str = pq_getmsgtext(fc_buf, fc_buf->len - fc_buf->cursor, &fc_nbytes);
	if (fc_nbytes >= NAMEDATALEN)
		ereport(ERROR,
				(errcode(ERRCODE_NAME_TOO_LONG),
				 errmsg("identifier too long"),
				 errdetail("Identifier must be less than %d characters.",
						   NAMEDATALEN)));
	fc_result = (NameData *) palloc0(NAMEDATALEN);
	memcpy(fc_result, fc_str, fc_nbytes);
	pfree(fc_str);
	PG_RETURN_NAME(fc_result);
}

/*
 *		namesend			- 将名称转换为二进制格式
 */
Datum namesend(PG_FUNCTION_ARGS)
{
	Name		fc_s = PG_GETARG_NAME(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendtext(&fc_buf, NameStr(*fc_s), strlen(NameStr(*fc_s)));
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}


/*****************************************************************************
 *	 比较/排序例程											 *
 *****************************************************************************/

/*
 *		nameeq	- 如果参数相等，则返回 1
 *		namene	- 如果参数不相等，则返回 1
 *		namelt	- 如果 a < b，则返回 1
 *		namele	- 如果 a <= b，则返回 1
 *		namegt	- 如果 a > b，则返回 1
 *		namege	- 如果 a >= b，则返回 1
 *
 * 请注意，使用带有 NAMEDATALEN 限制的 strncmp 大多是历史原因；
 * strcmp 也是可以的，因为我们不允许没有 '\0' 终止符的 NAME 值。
 * 终止符后的内容在比较中不被视为相关。
 */
static int fc_namecmp(Name fc_arg1, Name fc_arg2, Oid fc_collid)
{
	/* 用于系统目录常见情况的快速路径 */
	if (fc_collid == C_COLLATION_OID)
		return strncmp(NameStr(*fc_arg1), NameStr(*fc_arg2), NAMEDATALEN);

	/* 否则依赖 varstr 基础设施 */
	return varstr_cmp(NameStr(*fc_arg1), strlen(NameStr(*fc_arg1)),
					  NameStr(*fc_arg2), strlen(NameStr(*fc_arg2)),
					  fc_collid);
}

Datum nameeq(PG_FUNCTION_ARGS)
{
	Name		fc_arg1 = PG_GETARG_NAME(0);
	Name		fc_arg2 = PG_GETARG_NAME(1);

	PG_RETURN_BOOL(fc_namecmp(fc_arg1, fc_arg2, PG_GET_COLLATION()) == 0);
}

Datum namene(PG_FUNCTION_ARGS)
{
	Name		fc_arg1 = PG_GETARG_NAME(0);
	Name		fc_arg2 = PG_GETARG_NAME(1);

	PG_RETURN_BOOL(fc_namecmp(fc_arg1, fc_arg2, PG_GET_COLLATION()) != 0);
}

Datum namelt(PG_FUNCTION_ARGS)
{
	Name		fc_arg1 = PG_GETARG_NAME(0);
	Name		fc_arg2 = PG_GETARG_NAME(1);

	PG_RETURN_BOOL(fc_namecmp(fc_arg1, fc_arg2, PG_GET_COLLATION()) < 0);
}

Datum namele(PG_FUNCTION_ARGS)
{
	Name		fc_arg1 = PG_GETARG_NAME(0);
	Name		fc_arg2 = PG_GETARG_NAME(1);

	PG_RETURN_BOOL(fc_namecmp(fc_arg1, fc_arg2, PG_GET_COLLATION()) <= 0);
}

Datum namegt(PG_FUNCTION_ARGS)
{
	Name		fc_arg1 = PG_GETARG_NAME(0);
	Name		fc_arg2 = PG_GETARG_NAME(1);

	PG_RETURN_BOOL(fc_namecmp(fc_arg1, fc_arg2, PG_GET_COLLATION()) > 0);
}

Datum namege(PG_FUNCTION_ARGS)
{
	Name		fc_arg1 = PG_GETARG_NAME(0);
	Name		fc_arg2 = PG_GETARG_NAME(1);

	PG_RETURN_BOOL(fc_namecmp(fc_arg1, fc_arg2, PG_GET_COLLATION()) >= 0);
}

Datum btnamecmp(PG_FUNCTION_ARGS)
{
	Name		fc_arg1 = PG_GETARG_NAME(0);
	Name		fc_arg2 = PG_GETARG_NAME(1);

	PG_RETURN_INT32(fc_namecmp(fc_arg1, fc_arg2, PG_GET_COLLATION()));
}

Datum btnamesortsupport(PG_FUNCTION_ARGS)
{
	SortSupport fc_ssup = (SortSupport) PG_GETARG_POINTER(0);
	Oid			fc_collid = fc_ssup->ssup_collation;
	MemoryContext fc_oldcontext;

	fc_oldcontext = MemoryContextSwitchTo(fc_ssup->ssup_cxt);

	/* 使用通用字符串 SortSupport */
	varstr_sortsupport(fc_ssup, NAMEOID, fc_collid);

	MemoryContextSwitchTo(fc_oldcontext);

	PG_RETURN_VOID();
}


/*****************************************************************************
 *	 其他公共例程											 *
 *****************************************************************************/

void namestrcpy(Name fc_name, const char *fc_str)
{
	/* 注意：我们需要对目标进行零填充。 */
	strncpy(NameStr(*fc_name), fc_str, NAMEDATALEN);
	NameStr(*fc_name)[NAMEDATALEN - 1] = '\0';
}

/*
 * 将 NAME 与 C 字符串进行比较
 *
 * 假定始终使用 C 排序；在使用此函数进行除相等检查之外的操作时请小心！
 */
int namestrcmp(Name fc_name, const char *fc_str)
{
	if (!fc_name && !fc_str)
		return 0;
	if (!fc_name)
		return -1;				/* NULL < 任何值 */
	if (!fc_str)
		return 1;				/* NULL < 任何值 */
	return strncmp(NameStr(*fc_name), fc_str, NAMEDATALEN);
}


/*
 * SQL-函数 CURRENT_USER, SESSION_USER
 */
Datum current_user(PG_FUNCTION_ARGS)
{
	PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(GetUserId(), false))));
}

Datum session_user(PG_FUNCTION_ARGS)
{
	PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(GetSessionUserId(), false))));
}


/*
 * SQL-函数 CURRENT_SCHEMA, CURRENT_SCHEMAS
 */
Datum current_schema(PG_FUNCTION_ARGS)
{
	List	   *fc_search_path = fetch_search_path(false);
	char	   *fc_nspname;

	if (fc_search_path == NIL)
		PG_RETURN_NULL();
	fc_nspname = get_namespace_name(linitial_oid(fc_search_path));
	list_free(fc_search_path);
	if (!fc_nspname)
		PG_RETURN_NULL();		/* 最近删除的命名空间？ */
	PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(fc_nspname)));
}

Datum current_schemas(PG_FUNCTION_ARGS)
{
	List	   *fc_search_path = fetch_search_path(PG_GETARG_BOOL(0));
	ListCell   *fc_l;
	Datum	   *fc_names;
	int			fc_i;
	ArrayType  *fc_array;

	fc_names = (Datum *) palloc(list_length(fc_search_path) * sizeof(Datum));
	fc_i = 0;
	foreach(fc_l, fc_search_path)
	{
		char	   *fc_nspname;

		fc_nspname = get_namespace_name(lfirst_oid(fc_l));
		if (fc_nspname)			/* 注意已删除的命名空间 */
		{
			fc_names[fc_i] = DirectFunctionCall1(namein, CStringGetDatum(fc_nspname));
			fc_i++;
		}
	}
	list_free(fc_search_path);

	fc_array = construct_array(fc_names, fc_i,
							NAMEOID,
							NAMEDATALEN,	/* sizeof(Name) */
							false,	/* Name 不是按值传递 */
							TYPALIGN_CHAR); /* Name 的对齐方式 */

	PG_RETURN_POINTER(fc_array);
}

/*
 * SQL-函数 nameconcatoid(name, oid) 返回名称
 *
 * 这在 information_schema 中用于生成 specific_name 列，
 * 这些列应在每个模式中是唯一的。我们通过附加对象的 OID 来实现这一点（用一种丑陋的方式）。
 * 结果与
 *		($1::text || '_' || $2::text)::name
 * 是相同的，除了如果它不能适应 NAMEDATALEN，我们通过截断名称输入（而不是 OID）来使其适应。
 */
Datum nameconcatoid(PG_FUNCTION_ARGS)
{
	Name		fc_nam = PG_GETARG_NAME(0);
	Oid			fc_oid = PG_GETARG_OID(1);
	Name		fc_result;
	char		fc_suffix[20];
	int			fc_suflen;
	int			fc_namlen;

	fc_suflen = snprintf(fc_suffix, sizeof(fc_suffix), "_%u", fc_oid);
	fc_namlen = strlen(NameStr(*fc_nam));

	/* 通过截断名称部分而不是后缀来截断超长输入 */
	if (fc_namlen + fc_suflen >= NAMEDATALEN)
		fc_namlen = pg_mbcliplen(NameStr(*fc_nam), fc_namlen, NAMEDATALEN - 1 - fc_suflen);

	/* 我们在这里使用 palloc0 以确保结果是零填充的 */
	fc_result = (Name) palloc0(NAMEDATALEN);
	memcpy(NameStr(*fc_result), NameStr(*fc_nam), fc_namlen);
	memcpy(NameStr(*fc_result) + fc_namlen, fc_suffix, fc_suflen);

	PG_RETURN_NAME(fc_result);
}
